package chartCraft.graphDesk;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.Observer;

import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.border.Border;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;

import chartCraft.Log;
import chartCraft.model.Model;


/**
 * MySwing: Advanced Swing Utilites
 * Copyright (C) 2005 Santhosh Kumar T
 * <p/>
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p/>
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * @author Santhosh Kumar T
 * @author ctldenon ctldenon@gmail.com
 */
public abstract class DraggableItem extends JComponent implements Observer{

	private static final long serialVersionUID = 1L;
	private ResizableBorder mResizableBorder = null;

	protected abstract void beingSelected();

	public abstract void setModel(Model model);
	public abstract Model getModel();
	public abstract void draggingEnd(Point startPoint, Point endPoint, Dimension startSize, Dimension endSize);
	public abstract JPopupMenu getPopUpMenu();
	
	public DraggableItem(Component comp) {
		this();
		add(comp);
	}

	public DraggableItem() {
		setLayout(new BorderLayout());
		setBorder(new DefaultResizableBorder(6));
	}



	public void moveTo(Point newLocation) {
		if (newLocation != null) {
			final Point currentLocaiton = getLocation();
			if (!currentLocaiton.equals(newLocation)) {
				setLocation(newLocation);
				didResized();
			}
		}
	}

	public void resizeTo(Dimension newSize) {
		if (newSize != null) {
			final Dimension currentSize = getSize();
			if (!currentSize.equals(newSize)) {
				setSize(newSize);
				didResized();
			}
		}
	}

	public void showResizableBorder(boolean flag) {
		if (mResizableBorder != null) {
			mResizableBorder.showBorder(flag);
			getParent().repaint();
		}
	}

	public void setBorder(Border border) {
		mResizableBorder = null;
		removeMouseListener(resizeListener);
		removeMouseMotionListener(resizeListener);
		if(border instanceof ResizableBorder){
			addMouseListener(resizeListener);
			addMouseMotionListener(resizeListener);
			mResizableBorder = (ResizableBorder) border;
		}
		super.setBorder(border);
	}

	private void didResized(){
		if(getParent()!=null){
			getParent().repaint();
			invalidate();
			((JComponent)getParent()).revalidate();
		}
	}

	MouseInputListener resizeListener = new MouseInputAdapter(){
		
		private void bringContextSensitiveMenu(MouseEvent me) {
			JPopupMenu menu = DraggableItem.this.getPopUpMenu();
			if (menu != null) {
				menu.show(me.getComponent(), me.getX(), me.getY());
			}
		}
		
		public void mouseMoved(MouseEvent me){
			ResizableBorder border = (ResizableBorder)getBorder();
			setCursor(Cursor.getPredefinedCursor(border.getResizeCursor(me)));
		}

		public void mouseExited(MouseEvent mouseEvent){
			setCursor(Cursor.getDefaultCursor());
		}

		private int cursor;
		private Point relativeStartPoint = null;
		private Point absLocationBeforeMove = null;
		private Dimension sizeBeforeResize = null;


		public void mousePressed(MouseEvent me){
			if (me.isPopupTrigger()) {
				bringContextSensitiveMenu(me);
				beingSelected();
			} else {
				//prepare for the mouse dragging
				ResizableBorder border = (ResizableBorder)getBorder();
				cursor = border.getResizeCursor(me);
				relativeStartPoint = me.getPoint();
				absLocationBeforeMove = getLocation();
				sizeBeforeResize = getSize();
				beingSelected();
			}
		}

		public void mouseDragged(MouseEvent me){

			if(relativeStartPoint!=null){
				int dx = me.getX()-relativeStartPoint.x;
				int dy = me.getY()-relativeStartPoint.y;
				int newHeight;
				int newWidth;
				switch(cursor){
				case Cursor.N_RESIZE_CURSOR:
					newHeight = getHeight() - dy;
					if (newHeight > mResizableBorder.getThickness()) {
						setBounds(getX(), getY()+dy, getWidth(), newHeight);
						didResized();
					}
					break;

				case Cursor.S_RESIZE_CURSOR:
					newHeight = getHeight() + dy;
					if (newHeight > mResizableBorder.getThickness()) {
						setBounds(getX(), getY(), getWidth(), newHeight);
						relativeStartPoint = me.getPoint();
						didResized();
					}
					break;
				case Cursor.W_RESIZE_CURSOR:
					newWidth = getWidth() - dx;
					if (newWidth > mResizableBorder.getThickness()) {
						setBounds(getX()+dx, getY(), newWidth, getHeight());
						didResized();
					}
					break;
				case Cursor.E_RESIZE_CURSOR:
					newWidth = getWidth() + dx;
					if (newWidth > mResizableBorder.getThickness()) {
						setBounds(getX(), getY(), newWidth, getHeight());
						relativeStartPoint = me.getPoint();
						didResized();
					}
					break;
				case Cursor.NW_RESIZE_CURSOR:
					newHeight = getHeight() - dy;
					newWidth = getWidth() - dx;
					if (newHeight > mResizableBorder.getThickness() && 
							newWidth > mResizableBorder.getThickness()) {
						setBounds(getX()+dx, getY()+dy, newWidth, newHeight);
						didResized();
					}
					break;
				case Cursor.NE_RESIZE_CURSOR:
					newHeight = getHeight() - dy;
					newWidth = getWidth() + dx;
					if (newHeight > mResizableBorder.getThickness() && 
							newWidth > mResizableBorder.getThickness()) {
						setBounds(getX(), getY()+dy, newWidth, newHeight);
						relativeStartPoint = new Point(me.getX(), relativeStartPoint.y);
						didResized();
					}
					break;
				case Cursor.SW_RESIZE_CURSOR:
					newHeight = getHeight() + dy;
					newWidth = getWidth() - dx;
					if (newHeight > mResizableBorder.getThickness() && 
							newWidth > mResizableBorder.getThickness()) {
						setBounds(getX()+dx, getY(), newWidth, newHeight);
						relativeStartPoint = new Point(relativeStartPoint.x, me.getY());
						didResized();
					}
					break;
				case Cursor.SE_RESIZE_CURSOR:
					newHeight = getHeight() + dy;
					newWidth = getWidth() + dx;
					if (newHeight > mResizableBorder.getThickness() && 
							newWidth > mResizableBorder.getThickness()) {
						setBounds(getX(), getY(), newWidth, newHeight);
						relativeStartPoint = me.getPoint();
						didResized();
					}
					break;
				case Cursor.MOVE_CURSOR:
					Rectangle bounds = getBounds();
					bounds.translate(dx, dy);
					setBounds(bounds);
					didResized();
				}

				// cursor shouldn't change while dragging
				setCursor(Cursor.getPredefinedCursor(cursor));
			}
		}

		public void mouseReleased(MouseEvent mouseEvent){
			if (mouseEvent.isPopupTrigger()) {
				bringContextSensitiveMenu(mouseEvent);
			}
			final Point finalLocation = getLocation();
			final Dimension finalSize = getSize();
			if (!finalLocation.equals(absLocationBeforeMove) || !finalSize.equals(sizeBeforeResize)) {
				draggingEnd(absLocationBeforeMove, finalLocation, sizeBeforeResize, finalSize);
			}
			relativeStartPoint = null;
			absLocationBeforeMove = null;
		}
	};
}

